﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using QntPlatform.Strategy;
using static QntPlatform.Strategy.CmdCtrl.StopLoss;

namespace QntPlatform.Strategy.CmdCtrl
{
    /*
     * 行情分析
     * 触发命令
     * 分析命令
     * 买入：行情信息、账户信息
     * 设置止损
     * 设置止盈
     * 监测止损
     * 监测止盈
     * ------------
     * 管道添加--事件处理
     * 判断趋势
     * 交易：仓位计算
     * 计算止损
     * 计算止盈
     * 监控添加
     * */
    /// <summary>
    /// 普通信号做单，使用2步
    ///  收到信息：多空、止盈
    /// 1.按照信号止盈一半，设置止损为成交价
    /// 2.跟踪止损
    /// 
    /// </summary>
    public class TrailingStep2 : CmdHandlePart, ICmdCtrl
    {
        decimal stopLossRatio = 0.02m;

       
        public TrailingStep2(StrategyBase parent) : base(parent)
        {

        }
 
        EventHandler<Ticker> ev;
        public override void OnExit(Exception ex)
        {
            Exchange.TickerChangeEvent -= ev;
        }





        public bool Execute(CmdContext context)
        {
            var cmdVal = GetCmdValue<TradeCmd>(context.Cmd.Value);
            //判断趋势
            if (!trendCheck(cmdVal.Period, cmdVal.IsLong))
            {
                log.Debug("趋势错误，忽略", context.CmdStr);
                return true;
            }

            var ret = Exchange.GetRecordsAsync(cmdVal.Period);
            var account = Exchange.GetAccountAsync();
            var tck = Exchange.GetTickerAsync();
            //计算止损：数量，止损价，止损类型(固定）
            decimal stopLoss = StopLossCalc(ret.Result, cmdVal.IsLong);
            //交易：仓位计算
            var nowPrice = tck.Result.Buy;
            decimal buyAmount = AmountCalc(account.Result, stopLoss, nowPrice, cmdVal.IsLong);
            if (buyAmount <= 0)
            {
                log.Warn("仓位无效，空执行处理", new { account.Result, stopLoss, nowPrice, cmdVal.IsLong, context.CmdStr });
                return true;
            }
            var id = execTrade(DirectionTo.InFor(cmdVal.IsLong), buyAmount, new {nowPrice, Sid = GetHashCode() });//   Exchange.BuyAsync(buyAmount) : Exchange.SellAsync(-1, buyAmount);  // execTrade(DirectionTo.InFor(cmdVal.IsLong), buyAmount);
            //计算止盈：数量，止盈价，止盈类型(跟踪、固定)，止盈后动作(设置止损)

            var spl = new StopLoss(this, new StopLossInfo() { Amount = buyAmount, StopLossPrice = stopLoss, IsCloseBuy = cmdVal.IsLong, Info = GetHashCode() });
            var fp = new FixedProfit(this) { Amount = Exchange.getAmount(buyAmount / 2), Price = cmdVal.sells[0], IsLong = cmdVal.IsLong, Sid = "" + GetHashCode() };
            fp.AfterFun = () => spl.Data.StopLossPrice = nowPrice;
            var tp = new TrailingProfit(this, new TrailingProfit.TrailingProfitData() { Sid = GetHashCode() + "", Amount = buyAmount - Exchange.getAmount(buyAmount / 2), InPrice = nowPrice, CloseDirection = DirectionTo.CloseFor(cmdVal.IsLong) });
            tp.SetProfitRate(cmdVal.sells[0]);
            var isTpBegin = false;
            var fun = new MiddleGuoup<Ticker>()
                   .AddUse(p =>
                   {
                       var re = spl.Execute(p);
                       if (re) OnExit(null);
                       return re;
                   })
                   .AddUse(p =>
                   {
                       if (isTpBegin) return false;
                       var re = fp.Execute(p);
                       if (re) { 
                           isTpBegin = true;
                           spl.Data.StopLossPrice = nowPrice;
                       }
                       return false;
                   })
                   .AddRun(p =>
                   {
                       if (isTpBegin)
                       {
                           var re = tp.Execute(p);
                           if (re) OnExit(null);
                       }
                   })
                   .Bulid();
            ev = (p1, p2) => fun.Invoke(p2);
            Exchange.TickerChangeEvent += ev;
            //监控添加
            return true;
        }

        private decimal AmountCalc(Account account, decimal stopLoss, decimal nowPrice,bool isLong)
        {
            var longVal = isLong ? 1 : -1;
            var am= longVal*account.Balance * stopLossRatio / (nowPrice-stopLoss);
            return Exchange.getAmount(am);
        }

        private decimal StopLossCalc(Record[] records, bool isLong)
        {
            var last7 = records.Skip(records.Length - 7); // Aggregate((p1, p2) =>(p1 == null || p1.Low > p2.Low)?p2:p1);
            var top = isLong ? last7.Min(p => p.Low) : last7.Max(p => p.High);
            var atr = (decimal)TA.ATR(records, 14).Last();
            var atrV = isLong ? -atr * 0.5m : atr * 0.5m;
            var stopLoss = top + atrV; //止损价
            log.Debug("止损计算信息", new { stopLoss, top, atr});
            return stopLoss;
        }


        public class FixedProfit : StrategyPart
        {
            public FixedProfit(IStrategy parent) : base(parent)
            {
            }
            public decimal Price { get; set; }
            public decimal Amount { get; set; }
            public bool IsLong { get; set; }
            public Action AfterFun { get; set; }

            public bool IsActive = true;
            private string sid;
            public string SourceOrderId { get; set; }
            public string Sid { get => sid; set => sid = value+"-"+GetHashCode(); }
            public bool Execute(Ticker tck)
            {
                lock (this)
                {
                    if (!IsActive)
                        return false;
                    if (ISysFun.calcUnitProfit(tck.Buy, Price, IsLong) >= 0)
                    {
                        Log.Default().Debug("固定止盈执行", new { Price, Amount });
                        //var id = execTrade(DirectionTo.CloseFor(IsLong), Amount,new { Sid});
                        Exchange.CreateOrder(Amount, DirectionTo.CloseFor(IsLong),info:new Db.LogInfo() {ParentActId=SourceOrderId,Info2="固定止盈,市价:"+tck.Buy });
                        IsActive = false;
                        return true;

                    }
                    return false;
                }
            }
        }
        // bool trendCheck(int period,bool isLong) {
        //     var trend = getTrend(period);
        //     if (isLong)
        //     {
        //         return trend is 1 or 2;
        //     }
        //     else
        //     {
        //         return trend is -1 or 2;
        //     }
        // }

        public class TradeCmd
        {
            public decimal? Price { get; set; }
            public decimal[] sells { get; set; }
            public int Period { get; set; }
            public bool IsLong { get; set; }
        }
    }
}
